#include <string.h>
#include <stdlib.h>

#include "include/dynamic_list.h"
#include "include/logger.h"

/*
 *	dynlist_t is used only for keeping track of pointers. The actual data 
 * 	must be allocated manually and the same type of data must be stored in
 *	each list node because they all share a (user-specified) data 
 *	deallocation method
 */


/* Functions */

/* Create a list */
dynlist_t * dynlist_create(char *name, void (*free_func)(void *))
{
	dynlist_t * result = malloc(sizeof(dynlist_t));
	result->name = name;
	result->count = 0;
	result->free_data = free_func;
	result->root = NULL;
	return result;
}

/*
 * dynlist_free frees the memory taken up by the list (frees all its nodes
 * and the list itself) and its data (calls the function specified as the second
 * param. for each node, passing the node's data to the function, so it can 
 * deal with the data as it sees fit)
 */
void dynlist_free(dynlist_t ** list)
{
	if(*list == NULL) {
		return;
	}
	/* Iterate through the list, free all the nodes and their data */
	dynlist_node_t * temp = NULL;
	for(dynlist_node_t * n = (*list)->root; n != NULL; n = n->next)
	{
		/* Delete the previous node */
		if(temp) {
			free(temp);
		}
			
		/* Delete the current node's data */
		if(n->data) {
			(*list)->free_data(n->data);
		}
			
		/* Keep track of this node so we can delete it next */
		temp = n;
	}
	free(temp);
	free(*list);
	*list = NULL;
}

void dynlist_push(dynlist_t *list, void *data)
{
	if(list == NULL) return;
	
	/* Attach an item to the end of the list */
	dynlist_node_t * ptr;
	for(ptr = list->root; (ptr != NULL && ptr->next != NULL); ptr = ptr->next);
	
	if(ptr != NULL) {
		/* Found the last item, add a new one to the end */
		ptr->next = malloc(sizeof(dynlist_node_t));
		
		/* Point back but not forward */
		ptr->next->prev = ptr;
		ptr->next->next = NULL;
		
		ptr = ptr->next;
	} else {
		/* There are no items, initialize root */
		ptr = malloc(sizeof(dynlist_node_t));
		list->root = ptr;
		
		/* Point neither back nor forward */
		ptr->next = NULL;
		ptr->prev = NULL;
	}
	
	/* Make it point to the data and terminate with NULL */
	ptr->data = data;
	list->count += 1;	
}

void * dynlist_get(dynlist_t * list, int i)
{
	if(list == NULL) return NULL;
	dynlist_node_t * ptr = list->root;
	for(int I = 0; I < i; I++)
		ptr = ptr->next;
	return ptr->data;
}

void dynlist_remove(dynlist_t * list, int i)
{
	if(list == NULL || list->root == NULL) return;
	dynlist_node_t * ptr = list->root;
	for(int I = 0; I < i; I++)
		ptr = ptr->next;
		
	/* Remove the node from the chain */
	if(ptr->prev)
	{
		/* not root */
		if(ptr->next) {
			ptr->prev->next = ptr->next;
			ptr->next->prev = ptr->prev;
		} else
			ptr->prev->next = NULL;
	}
	else
	{
		/* root */
		if(ptr->next) {
			list->root = ptr->next;
		} else
			list->root = NULL;
	}
	
	list->count -= 1;
	list->free_data(ptr->data);
	free(ptr);
}

void * dynlist_pop(dynlist_t *list)
{
	if(list == NULL || list->count == 0) return NULL;
	
	/* Skip to the last data */
	dynlist_node_t * ptr;
	for(ptr = list->root; ptr->next != NULL; ptr = ptr->next);
	
	/* Take this node out of the list, return its data and delete it */
	if(ptr->prev)
		ptr->prev->next = NULL;
	void * result = ptr->data;
	free(ptr);
	
	list->count -= 1;
	if(list->count == 0)
		list->root = NULL;	
	
	return result;
}
